package com.zfsmart.component.common.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;

@Component
@Slf4j
public class DealKeyUtil {
    private static final String DES_CBC_NO_PADDING = "DES/CBC/NoPadding";

    private final static String DEFAULT_ENCODING = "GBK";

    private IvParameterSpec iv;    //加密向量
    private HashMap<String, SecretKeySpec> desKeyList = new HashMap<>();    //加密key

    public void init(String key, String encoding) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException {
        if (iv == null) {
            byte[] iv1 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};    //初始向量
            iv = new IvParameterSpec(iv1);
        }
        if (!desKeyList.containsKey(key)) {
            if (StringUtils.isBlank(encoding)) {
                encoding = DEFAULT_ENCODING;
            }
            SecretKeySpec desKey = new SecretKeySpec(key.getBytes(encoding), "DES");
            desKeyList.put(key, desKey);
        }
    }

    public String encrpyt(String text, String key, String encoding) {
        try {
            init(key, encoding);
        } catch (UnsupportedEncodingException e) {
            log.error("加密工具初始化异常{}", e);
            return null;
        } catch (NoSuchAlgorithmException e) {
            log.error("加密工具初始化异常{}", e);
            return null;
        } catch (NoSuchPaddingException e) {
            log.error("加密工具初始化异常{}", e);
            return null;
        }
        log.debug("data={}", text);
        try {
            Cipher cipher = Cipher.getInstance(DES_CBC_NO_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, desKeyList.get(key), iv);
            byte[] data = cipher.doFinal(padding(text, encoding));
            String result = Hex.encodeHexString(data).toUpperCase();

            log.debug("data={} cipher={}", text, result);

            return result;
        } catch (Exception e) {
            log.info("error.dealkey.encrpyt.failed", e);
        }
        return null;
    }

    public String decrypt(String data, String key, String encoding) {
        try {
            init(key, encoding);
        } catch (UnsupportedEncodingException e) {
            log.error("加密工具初始化异常{}", e);
            return null;
        } catch (NoSuchAlgorithmException e) {
            log.error("加密工具初始化异常{}", e);
            return null;
        } catch (NoSuchPaddingException e) {
            log.error("加密工具初始化异常{}", e);
            return null;
        }

        log.debug("cipher={}", data);
        try {
            Cipher cipher = Cipher.getInstance(DES_CBC_NO_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, desKeyList.get(key), iv);
            byte[] bytes = cipher.doFinal(Hex.decodeHex(data.toCharArray()));
            int length = bytes.length;
            for (int i = bytes.length - 1; i >= 0; i--) {
                if (bytes[i] == '\0') {
                    length--;
                }
            }
            String result = new String(Arrays.copyOf(bytes, length), encoding);

            log.debug("cipher={} data={}", data, result);

            return result;
        } catch (Exception e) {
            log.info("error.dealkey.decrpyt.failed", e);
        }
        return null;
    }

    private byte[] padding(String text, String encoding) throws UnsupportedEncodingException {
        byte[] data = text.getBytes(encoding);
        int length = (data.length / 8 + 1) * 8;
        byte[] paddingData = Arrays.copyOf(data, length);
        return paddingData;
    }

    /**
     * 时间校验
     */
    public boolean timeVerify(String date) {
        Date now = new Date();
        //设置要获取到什么样的时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        //获取String类型的时间
        try {
            return (now.getTime() - sdf.parse(date).getTime()) <= 5 * 60 * 1000;
        } catch (ParseException e) {
            log.error("date pase failed", e);
        }
        return false;
    }

}
